package com.avalon;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.jar.Attributes;
import java.util.jar.Attributes.Name;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.jar.Manifest;

/** 用于确定排序的扩展jar文件的集合 */
class ExtJarGraph {

	// 扩展属性配置文件的位置
	static final String EXT_PROPERTIES_FILE = "META-INF/ext.properties";

	// 属性依赖配置
	static final String DEPENDENCY_PROPERTY = "DEPENDS_ON";

	// 扩展jar的图
	private final Map<String, JarNode> extNodes = new HashMap<String, JarNode>();

	// 依赖jar的集合
	private final Set<JarNode> dependencyRoots = new HashSet<JarNode>();

	// 是否有任何偏好或依赖关系的定义的集扩展jar文件
	private boolean hasPreferences = false;
	private boolean hasDependencies = false;

	/** Creates an instance of {@code ExtJarGraph}. */
	ExtJarGraph()
	{}

	/**
	 * 添加一个jar文件并检查它的合法性
	 * 
	 * @param jar
	 *            the extension jar to add
	 */
	void addJarFile(JarFile jar)
	{
		JarNode node = null;

		// 提取清单的名称和版本，并确保该名称是唯一的
		Manifest manifest = null;
		try
		{
			manifest = jar.getManifest();
		} catch (IOException ioe)
		{
			// 获取不到jar的文件清单
			throw new IllegalStateException("Failed to get manifest from " + "jar file: " + jar.getName());
		}
		if (manifest == null)
		{
			// jar文件中没有文件清单
			throw new IllegalStateException("Manifest missing in extension " + "jar file: " + jar.getName());
		}
		// 获得主Attributes
		Attributes attrs = manifest.getMainAttributes();
		// 获得版本控制属性
		String extName = attrs.getValue(Name.SPECIFICATION_TITLE);
		if (extName == null)
		{
			throw new IllegalStateException("Specification name required");
		}
		if (extNodes.containsKey(extName))
		{
			throw new IllegalStateException("Found two extensions with the " + "same name: " + extName);
		}
		String extVersion = attrs.getValue(Name.SPECIFICATION_VERSION);
		if (extVersion == null)
		{
			throw new IllegalStateException("Specification version required");
		}

		// 查看该jar文件包含的任何属性
		JarEntry propertiesEntry = jar.getJarEntry(EXT_PROPERTIES_FILE);
		if (propertiesEntry != null)
		{
			Properties properties = new Properties();
			try
			{
				properties.load(jar.getInputStream(propertiesEntry));
			} catch (IOException ioe)
			{
				throw new IllegalStateException("Malformed properties in " + "ext jar: " + jar.getName());
			}
			node = new JarNode(extName, extVersion, properties);
			hasPreferences = true;

			String dependencies = (String) properties.remove(DEPENDENCY_PROPERTY);
			if (dependencies != null)
			{
				hasDependencies = true;
				for (String dependency : dependencies.split(":"))
				{
					node.namedDependencies.add(dependency);
				}
			}
		} else
		{
			node = new JarNode(extName, extVersion);
		}

		extNodes.put(extName, node);
		dependencyRoots.add(node);
	}

	/**
	 * 返回一个包含所有的jar文件的集合，或者为null中定义的属性，如果没有属性定义一个文件的名称。这种方法将确保满足所有相关性，
	 * 不存在任何循环依赖，而属性是否正确定义。
	 * 
	 * @return a property filename or null
	 * @throws IOException
	 *             if there is a problem creating the property file
	 */
	String getPropertiesFile() throws IOException
	{
		if (extNodes.isEmpty() || (!hasPreferences))
		{
			return null;
		}
		if (hasDependencies)
		{
			checkDependencies();
		}

		// collect list properties in the right order
		Properties properties = new Properties();
		StringBuilder servicesLine = new StringBuilder();
		StringBuilder managersLine = new StringBuilder();
		StringBuilder nodeTypesLine = new StringBuilder();
		StringBuilder authenticatorsLine = new StringBuilder();
		StringBuilder profileListenersLine = new StringBuilder();
		for (JarNode node : dependencyRoots)
		{
			buildProperties(node, properties, servicesLine, managersLine, nodeTypesLine, authenticatorsLine,
					profileListenersLine);
		}
		if (servicesLine.length() != 0)
		{
			properties.setProperty("com.zero.ava.ext.services", servicesLine.toString());
		}
		if (managersLine.length() != 0)
		{
			properties.setProperty("com.zero.ava.ext.managers", managersLine.toString());
		}
		if (nodeTypesLine.length() != 0)
		{
			properties.setProperty("com.zero.ava.ext.services.node.types", nodeTypesLine.toString());
		}
		if (authenticatorsLine.length() != 0)
		{
			properties.setProperty("com.zero.ava.ext.authenticators", authenticatorsLine.toString());
		}
		if (profileListenersLine.length() != 0)
		{
			properties.setProperty("com.zero.ava.ext.kernel.profile.listeners", profileListenersLine.toString());
		}

		// 生成属性文件
		File propFile = File.createTempFile("extProperties", null);
		FileOutputStream out = new FileOutputStream(propFile);
		try
		{
			properties.store(out, "Temporary extension properties file");
		} finally
		{
			out.close();
		}
		return propFile.getAbsolutePath();
	}

	/**
	 * 检查是否满足所有相关性，并且有没有循环依赖
	 */
	private void checkDependencies()
	{
		// 检查所有jar的依赖
		for (JarNode node : extNodes.values())
		{
			for (String dependency : node.namedDependencies)
			{
				JarNode dNode = extNodes.get(dependency);
				if (dNode == null)
				{
					throw new IllegalStateException("Missing dependency: " + dependency);
				}
				// 如果有人取决于dNode然后它从根集合中删除
				dependencyRoots.remove(dNode);
				node.dNodes.add(dNode);
			}
		}

		// 查看任何依赖
		if (extNodes.size() == dependencyRoots.size())
		{
			return;
		}

		// 确定没有循环依赖
		if (dependencyRoots.isEmpty())
		{
			throw new IllegalStateException("Circular dependency not allowed");
		}
		Set<String> names = new HashSet<String>();
		for (JarNode node : dependencyRoots)
		{
			names.clear();
			loopCheck(node, names);
		}
	}

	/** 递归地检查一个给定的节点不会导致环路。（递归检测） */
	private static void loopCheck(JarNode node, Set<String> names)
	{
		if (names.contains(node.name))
		{
			throw new IllegalStateException("Loop in dependent extensions: " + node.name);
		}
		names.add(node.name);
		for (JarNode dNode : node.dNodes)
		{
			loopCheck(dNode, new HashSet<String>(names));
		}
	}

	/**
	 * 收集所有的属性和多元素线.
	 * 
	 * @param node
	 *            jar的节点
	 * @param p
	 *            属性文件
	 * @param servicesLine
	 *            服务器
	 * @param managersLine
	 *            管理器
	 * @param nodeTypesLine
	 *            节点类型监听器
	 * @param authenticatorsLine
	 *            认证程序行
	 * @param profileListenersLine
	 *            概况监听器
	 */
	private void buildProperties(JarNode node, Properties p, StringBuilder servicesLine, StringBuilder managersLine,
			StringBuilder nodeTypesLine, StringBuilder authenticatorsLine, StringBuilder profileListenersLine)
	{
		// gather properties from depdencies first
		for (JarNode dNode : node.dNodes)
		{
			buildProperties(dNode, p, servicesLine, managersLine, nodeTypesLine, authenticatorsLine,
					profileListenersLine);
		}

		// include this node's properties if they haven't already been included
		if (extNodes.remove(node.name) != null)
		{
			// remove the standard list properties to combine in a
			// separate collection
			Properties nodeProps = node.properties;
			String managers = (String) nodeProps.remove("com.zero.ava.managers");
			int managerCount = getElementCount(managers);
			String services = (String) nodeProps.remove("com.zero.ava.services");
			int serviceCount = getElementCount(services);
			String nodeTypes = (String) nodeProps.remove("com.zero.ava.services.node.types");
			int nodeTypeCount = getElementCount(nodeTypes);

			// verify that the manager and service counts line up, or if
			// there are no managers then there is at most only one service
			if (managerCount != 0)
			{
				if (managerCount != serviceCount)
				{
					throw new IllegalStateException("Mis-matched Manager " + "and Service count for " + node.name);
				}
			} else
			{
				if (serviceCount > 1)
				{
					throw new IllegalStateException("Missing Managers for " + node.name);
				}
			}

			// verify that there are either no node types, or exactly the same
			// number as there are services
			if (nodeTypeCount != 0 && nodeTypeCount != serviceCount)
			{
				throw new IllegalStateException("Mis-matched Node Type " + "and Service count for " + node.name);
			}

			// if there are services then add them after figuring out how to
			// modify the manager and node types lines correctly
			if (serviceCount != 0)
			{
				if (managerCount == 0)
				{
					if (servicesLine.length() != 0)
					{
						// there are no new managers but there are previous
						// services so we need to pad a ":" to the line
						managersLine.append(":");
					}
				} else
				{
					if ((servicesLine.length() != 0) && (managersLine.length() == 0))
					{
						// there were previously services but no managers, so
						// pre-pend a ":" to the line
						addToLine(managersLine, ":" + managers);
					} else
					{
						// no padding is needed, just add the managers
						addToLine(managersLine, managers);
					}
				}

				if (nodeTypeCount == 0)
				{
					for (int i = 0; i < serviceCount; i++)
					{
						addToLine(nodeTypesLine, "ALL");
					}
				} else
				{
					addToLine(nodeTypesLine, nodeTypes);
				}

				addToLine(servicesLine, services);
			}

			String authenticators = (String) nodeProps.remove("com.zero.ava.app.authenticators");
			if ((authenticators != null) && (authenticators.length() != 0))
			{
				addToLine(authenticatorsLine, authenticators);
			}

			String profileListeners = (String) nodeProps.remove("com.zero.ava.impl.kernel.profile.listeners");
			if ((profileListeners != null) && (profileListeners.length() != 0))
			{
				addToLine(profileListenersLine, profileListeners);
			}

			// 合并任何剩余的属性，如果同样的属性是由独立的扩展分配不同的值失败
			for (String key : nodeProps.stringPropertyNames())
			{
				String value = (String) nodeProps.getProperty(key);
				Object oldValue = p.setProperty(key, value);
				if ((oldValue != null) && (!value.equals(oldValue)))
				{
					throw new IllegalStateException("Multiple values for " + "property: " + key);
				}
			}
		}
	}

	/** 统计字符在字符串中的数量 . */
	private int getElementCount(String str)
	{
		if ((str == null) || (str.length() == 0))
		{
			return 0;
		}
		int count = 0;
		int pos = -1;
		do
		{
			count++;
			pos = str.indexOf(':', pos + 1);
		} while (pos != -1);
		return count;
	}

	/** 将一个元素添加SringBuffer */
	private static void addToLine(StringBuilder buf, String str)
	{
		if (buf.length() != 0)
		{
			buf.append(":" + str);
		} else
		{
			buf.append(str);
		}
	}

	/** 用于维护状态为一个单一的扩展jar私有类。 */
	private static class JarNode {
		final String name;
		final String version;
		final Properties properties;
		final Set<String> namedDependencies = new HashSet<String>();
		final Set<JarNode> dNodes = new HashSet<JarNode>();

		JarNode(String name, String version)
		{
			this.name = name;
			this.version = version;
			this.properties = null;
		}

		JarNode(String name, String version, Properties properties)
		{
			this.name = name;
			this.version = version;
			this.properties = properties;
		}

		/**
		 * 以jar的名字来确定唯一性
		 */
		public boolean equals(Object o)
		{
			if (!(o instanceof JarNode))
			{
				return false;
			}
			return name.equals(((JarNode) o).name);
		}

		public int hashCode()
		{
			return name.hashCode();
		}
	}

}
